#include "ac-application.h"
#include "ns3/integer.h"
#include "ac_FileHeader.h"
#include "infoTableHeader.h"
#include "publicTableHeader.h"
#include "FragmentationHeader.h"
#include "ns3/basic-energy-source.h"
#include "ns3/mobility-module.h"
#include "ns3/netanim-module.h"
#include <math.h>

namespace ns3 {

NS_LOG_COMPONENT_DEFINE ("AirCacheApp");

NS_OBJECT_ENSURE_REGISTERED (AirCacheApplication);

TypeId
AirCacheApplication::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::AirCacheApplication")
    .SetParent<Application> ()
    .AddConstructor<AirCacheApplication> ()
    .AddAttribute ("DataRate", "The data rate in on state.",
                   DataRateValue (DataRate ("500kb/s")),
                   MakeDataRateAccessor (&AirCacheApplication::m_cbrRate),
                   MakeDataRateChecker ())
    .AddAttribute ("FI_LENGTH", "La dimensione del frame information",
                   UintegerValue (32),
                   MakeUintegerAccessor (&AirCacheApplication::frameInformationLength),
                   MakeUintegerChecker<uint32_t> (1))
    .AddAttribute ("X_COORD", "X_Coordinata punto di interesse",
                   UintegerValue (0),
                   MakeUintegerAccessor (&AirCacheApplication::aoi_x),
                   MakeUintegerChecker<uint32_t> (1))
    .AddAttribute ("Y_COORD", "Y_Coordinata punto di interesse ",
                   UintegerValue (10),
                   MakeUintegerAccessor (&AirCacheApplication::aoi_y),
                   MakeUintegerChecker<uint32_t> (1))
    .AddAttribute ("DIST", "Distanza dal punto di interesse",
                   UintegerValue (0),
                   MakeUintegerAccessor (&AirCacheApplication::dist),
                   MakeUintegerChecker<uint32_t> (1))
    .AddAttribute ("FileSize", "The size of the file to create",
                   UintegerValue (0),
                   MakeUintegerAccessor (&AirCacheApplication::fileSize),
                   MakeUintegerChecker<uint32_t> ())
    .AddAttribute ("FileName", "The names of the file to create",
                   StringValue ("0"),
                   MakeStringAccessor (&AirCacheApplication::fileName),
                   MakeStringChecker ())
    .AddAttribute ("AoIFile", "The name of the file that contains the area of Interest coordinates",
                   StringValue (""),
                   MakeStringAccessor (&AirCacheApplication::aoiFileName),
                   MakeStringChecker ())
    .AddAttribute ("Battery", 
                   "Set the Battery percentage of the node",
                   IntegerValue (0),
                   MakeUintegerAccessor (&AirCacheApplication::batteryPercentage),
                   MakeUintegerChecker<uint32_t> ())
    .AddAttribute ("NodeID", 
                   "The node identification number",
                   IntegerValue (-1),
                   MakeIntegerAccessor (&AirCacheApplication::node_id),
                   MakeIntegerChecker<int> ())
    .AddAttribute ("Protocol", "The type of protocol to use.",
                   TypeIdValue (UdpSocketFactory::GetTypeId ()),
                   MakeTypeIdAccessor (&AirCacheApplication::m_tid),
                   MakeTypeIdChecker ())
    .AddAttribute ("Energy", "Energy value",
                   PointerValue (),
                   MakePointerAccessor (&AirCacheApplication::basicSourcePtr),
                   MakePointerChecker<BasicEnergySource>())
    .AddTraceSource("FileOwner","Tell if node own a file",
                     MakeTraceSourceAccessor (&AirCacheApplication::ownFile)
    
    )
  ;

  return tid;
}





AirCacheApplication::AirCacheApplication ()
  : frameInfBeginTime(Seconds(0)),
    basicChannelLength(MilliSeconds(5)),
    initialized(false),
    sending(false),
    local(""),
    fileList(0),
    s_upper(0.75),
    s_lower(0.2),
    ownFile(false),
    lowerFILength(32),
    slotsToReserve(0),
    m_socket (0),
    s_socket(0),
    m_port(0),
    m_connected (false),
    m_residualBits (0),
    m_lastStartSendTime (Seconds (0)),
    m_totBytes (0)
    
    
    

{
NS_LOG_FUNCTION (this);
    
    
}




AirCacheApplication::~AirCacheApplication()
{
  NS_LOG_FUNCTION (this);
}

    
    void
    AirCacheApplication::DoDispose (void)
    {
        NS_LOG_FUNCTION (this);
        
        m_socket = 0;
        s_socket =0;
        // chain up
        Application::DoDispose ();
    }
void 
AirCacheApplication::frameInfNodeChoose(){
    //nel caso due nodi creano l'aircache nello stesso momento
    bool allFree=true;
    for(uint32_t i=0;i<frameInformationLength;i++)
    { if(frameInformation[i].free==false)
        allFree=false;
    }
    if(allFree)
    {
        frameInformation[0].free=false;
        frameInformation[0].node_name=node_id;
        basicChannelPosition=0;
    }
    else // non è il primo nodo quindi può scegliere un qualsiasi slot
    {


        //sceglie un numero random tra 0 e la lungheza del frame e vede se lo basicchannel è libero
        //se si lo riseva mettendo il suo id altrimenti ne sceglie un altro.
        std::vector<uint32_t> freePositions;
        for (uint32_t i=0; i<frameInformation.size(); i++) {
            if (frameInformation[i].free==true) {
                freePositions.push_back(i);
            }
        }
        
        if(freePositions.size()!=0)
        {
            bool esci=false;
            int x=0;
            int z=0;
            while(esci==false)
            {
                UniformVariable y (0,freePositions.size());
                
                z=y.GetValue();
                x=freePositions[z];
                
                if(frameInformation[x].free==true&&!esci)
                {
                    
                    frameInformation[x].free=false;
                    frameInformation[x].node_name=node_id;
                    esci=true;
                    basicChannelPosition=x;
                }
                
            }
        }
       


    }
    
}




void AirCacheApplication::stampaFI(std::vector<BasicChannel> frameInformation)
{
    
for(uint32_t i=0;i<frameInformation.size();i++)
{
    if(frameInformation[i].free==true)
        std::cout<<("[-]");
    else
        std::cout<<"["<<frameInformation[i].node_name<<"]";
}
    std::cout<<std::endl<<std::endl;
    NS_LOG_DEBUG("Dimensione fileList "<<fileList.size());
}



Ptr<Socket>
AirCacheApplication::GetSocket (void) const
{
  NS_LOG_FUNCTION (this);
  return m_socket;
}



// Application Methods 
void AirCacheApplication::StartApplication () // Called at time specified by Start
{
NS_LOG_FUNCTION (this);
    
    outFILength=frameInformationLength;

/***************************************************************************/
    if(fileName!="Vuoto")
    {
        ownFile=true;
 fileList.push_back(ACFile(fileSize,fileName,Buffer(fileSize)));
 ListOfFileNames.push_back(fileName);
    //ns3::BasicEnergySourceHelper basicSourceHelper;
   // basicSourceHelper.Set ("BasicEnergySourceInitialEnergyJ", DoubleValue (0.1));

    //source=basicSourceHelper.Install (this->GetNode());
    //deviceModels = radioEnergyHelper.Install (GetNode()->GetDevice(0), source);

    updatePublicTable(fileName,batteryPercentage,node_id);
    }
 /**************************************************************************/

/*CREARE i socket per send e listen

if (!s_socket)
    {
     s_socket = Socket::CreateSocket (GetNode (), m_tid);
      local = InetSocketAddress (GetNode()->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(),20);
      s_socket->Bind (local);
 
      s_socket->SetRecvCallback (MakeCallback (&AirCacheApplication::HandleRead, this));
    }


//send
if (!m_socket)
    { 

        m_socket = Socket::CreateSocket (GetNode (), m_tid);
        //local = InetSocketAddress (GetNode()->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(),20);

        m_socket->SetAllowBroadcast (true);
       // m_socket->Bind (local);
       // m_socket->SetRecvCallback (MakeCallback (&AirCacheApplication::HandleRead, this));
        m_socket->Connect (InetSocketAddress(Ipv4Address("255.255.255.255"), 20));
        
        m_socket->ShutdownRecv ();
        
    }
*/
 
    if(!nodeInAreaOfInterest())
    {
       m_looking_mode3=Simulator::Schedule (MilliSeconds(500), &AirCacheApplication::lookingMode, this);
    }
    else{
        CreateSockets();
//After a frame information length waiting time it is free to create a new air cache if one do not exist
    NS_LOG_INFO("START APPLICATION TIME NODE - : "<<node_id<<" AT TIME : "<<Simulator::Now().GetSeconds()<<" Schedule joinAirCache at : "<<MilliSeconds(5000));
    
    m_joining_ac1=Simulator::Schedule (MilliSeconds(5125), &AirCacheApplication::joinAirCache, this);
    }




}

void AirCacheApplication::init(){
  NS_LOG_FUNCTION (this);

//Parametri iniziali quando l'aircache è formato da un solo nodo
    uint64_t currTime=Simulator::Now().GetMilliSeconds();
    frameInfBeginTime=MilliSeconds(currTime);
    NS_LOG_INFO("frameInfBeginTime INIT"<<frameInfBeginTime);
    basicChannelLength=MilliSeconds(5);
    initialized=true;

for(uint32_t i=0;i<frameInformationLength;i++)
  frameInformation.push_back(BasicChannel(-1,true,MilliSeconds(5)));

//sceglie un basic channel
frameInfNodeChoose();





}
void AirCacheApplication::joinAirCache()
{
  
    if(initialized==false)
        {

           init(); 
           ScheduleStartTransmissionEvent ();   
           stampaFI(frameInformation);
        }
       
}

void AirCacheApplication::StopApplication () // Called at time specified by Stop
{
  NS_LOG_FUNCTION (this);
    
    printListofFileNames();
 
  if(m_socket != 0)
    {
      m_socket->Close ();
    }
  else
    {
      NS_LOG_WARN ("AirCacheApplication found null socket to close in StopApplication");
    }

if(s_socket != 0)
    {
      s_socket->Close ();
    }
  else
    {
      NS_LOG_WARN ("AirCacheApplication found null socket to close in StopApplication");
    }

   CancelSendEvents ();
    
         
}




void AirCacheApplication::CancelSendEvents ()
{
  NS_LOG_FUNCTION (this);
    
  Simulator::Cancel (m_sendEvent);
    
  Simulator::Cancel (m_startStopSendEvent);
    
  Simulator::Cancel (m_stopSending);
    
    Simulator::Cancel(m_looking_mode1);
   // Simulator::Cancel(m_looking_mode2);
    Simulator::Cancel(m_looking_mode3);
    
    
    Simulator::Cancel(m_joining_ac1);
    Simulator::Cancel(m_joining_ac2);

    
}






// Event handlers  //***3
void AirCacheApplication::StartSending ()
{
  NS_LOG_FUNCTION (this);


    sending=true;
  m_sendEvent = Simulator::Schedule (Seconds(0),
                                         &AirCacheApplication::SendPacket, this);
  
}






void AirCacheApplication::selfUpdateFI()
{
        

frameInfBeginTime+=MilliSeconds((frameInformationLength)*(basicChannelLength.GetMilliSeconds()));

 
}




void AirCacheApplication::StopSending ()
{
  NS_LOG_FUNCTION (this);

  Simulator::Cancel (m_sendEvent);

//aggiorna l'inizio del frame information time
  selfUpdateFI();

ScheduleStartTransmissionEvent ();

    sending=false;
}


void AirCacheApplication::ScheduleStartTransmissionEvent () //***2
{  
  NS_LOG_FUNCTION (this);
    Simulator::Cancel (m_startStopSendEvent);
  
  Time off = frameInfBeginTime + Seconds(basicChannelLength.ToDouble(Time::S)* basicChannelPosition);
  Time offInterval= off - Simulator::Now();
while(offInterval.GetSeconds()<0)
   {
        selfUpdateFI();
        NS_LOG_INFO("secondFIUPDATE "<<node_id<<" node");
        off = frameInfBeginTime + Seconds(basicChannelLength.ToDouble(Time::S)* basicChannelPosition);
        offInterval= off - Simulator::Now();
        NS_LOG_INFO("offInterval.GetSeconds() "<<offInterval.GetSeconds()<<" node "<<node_id);
   }

  m_startStopSendEvent = Simulator::Schedule (offInterval, &AirCacheApplication::StartSending, this);
}




void AirCacheApplication::ScheduleStopTransmissionEvent ()
{  
  NS_LOG_FUNCTION (this);
  m_stopSending=Simulator::Schedule (basicChannelLength, &AirCacheApplication::StopSending, this);
}


void AirCacheApplication::SendPacket ()
{
  NS_LOG_FUNCTION (this);
    NS_ASSERT (m_sendEvent.IsExpired ());
    
    // if the node is leaving the AoI then do not transmit

    listenedNodesSize=listenedNodes;
    memolistenedNodes=listenedNodes;
    std::vector<BasicChannel> fi;
    
    
 

    


    std::vector<Ptr<Packet> > packets;

   //La public table lo invia solo il nodo che ha la dimensione della public table più grande
     if(mostLinkedNode())
     {         
         packets.push_back(createPacket(3,0,fi));
         //printPublicTable();

     }
    
    


    
    //inserisci i file che sono sotto la soglia
    for(uint32_t i=0;i<fileList.size();i++)
    {
        for(uint32_t k=0;k<publicTable.size();k++)
        {

            if(publicTable[k].fileName==fileList[i].file_name)
            {

                if(publicTable[k].occurrences.size()<2)
                {
                    
                        
                  if(!filePresentInListenedNodes(fileList[i].file_name)&&!searchFileNameInOutGoingPackets(fileList[i].file_name) && listenedNodes.size()!=0)
                  {
                      packets.push_back(createPacket(4,i,fi));
                  }
                }
            }
        }
    }


   
    
    uint32_t bChThreshold=bytesSentInBasicChannel();
    
    
    fi=prepareOutgoingFI(listenedNodes,frameInformation);
    Ptr<Packet> frameInfAndInfoTab=createPacket(2,0,fi);

    //basic channel freeshold - the bytes used to send the frame information and the info table
   uint32_t restBytes=bChThreshold-(frameInfAndInfoTab->GetSize());

    
    /*PACKET FRAGMENTATION*/
    
for(uint32_t i=0;i<packets.size();i++)
    {
        std::vector<Ptr<Packet> > pack;
        /*In case we can send the packet in the default basic channel*/
        if(packets[i]->GetSize()<=restBytes)
        {
            restBytes+=packets[i]->GetSize();
            
        }
        /*otherwise, fragment it in packets begening from the end of the packet to the begining to facilitate the construction to the receiveing node*/
        else
        {
            
            uint32_t x=0;
            uint32_t p_size=packets[i]->GetSize();
            pack.push_back(packets[i]->CreateFragment (p_size-(x+bChThreshold), bChThreshold));
            
            outGoingPacketsList.push_back(OutGoingPackets(getfileNameInPacketType4(packets[i]),packets[i]->GetUid(),pack));
            
            
            
            x=x+bChThreshold;

            while((x+bChThreshold)<=p_size)
            {

                outGoingPacketsList.rbegin()->insertPacket(packets[i]->CreateFragment (p_size-(x+bChThreshold) ,bChThreshold));
                x+=bChThreshold;


            }

            /* Insert the rest bytes*/
            uint32_t k=p_size-x;
            if(k>0)
            {
                outGoingPacketsList.rbegin()->insertPacket(packets[i]->CreateFragment (0, k));
            }
            
            packets.erase(packets.begin()+i);
            
        }
        
    }

    
/*CALCULATE THE SIZE OF THE OUTGOING PACKETS*/
            uint32_t allOutGoingSize=0;
            for (uint32_t i=0; i<outGoingPacketsList.size(); i++) {
                allOutGoingSize+=outGoingPacketsList[i].outGoingSize();
            }
    
    
    
   
    
    
    /*CALCULATE THE NUMBER OF THE NEEDED SLOTS IN ORDER TO SEND ALL THE PACKETS*/
        slotsToReserve=calculateNumbOfSlotsToReserve(allOutGoingSize);
    /*Calculate the number of Unavailable slots*/
    double unAvailSlots=0;
    for (uint32_t i=0; i<frameInformation.size(); i++) {
        if(frameInformation[i].free==false)
        { unAvailSlots++;}
    }

    
    
    
    /*The halving process has to start before the slot reservation process for consistency reasons: For example: after the slot reservation process the condition unAvailSlots<s_lower*frameInformation.size() is still then the halving process will delete the just reserved slots in the slot reservation process*/
    
    
    while(unAvailSlots<s_lower*frameInformation.size()&&frameInformation.size()>lowerFILength)
    {
        halveFrameInformation();
        
        //prepare the new frame information
        fi=prepareOutgoingFI(listenedNodes,frameInformation);
        frameInfAndInfoTab=createPacket(2,0,fi);
        
    }
    
    unAvailSlots=0;
    for (uint32_t i=0; i<frameInformation.size(); i++) {
        if(frameInformation[i].free==false&&frameInformation[i].node_name!=-1)
            unAvailSlots++;
    }
    
    /*Check if the number of unavailable + solts to reserve exceed the number N*Supper value */
    
       if(unAvailSlots+slotsToReserve>(s_upper*frameInformationLength) && frameInformationLength<1024)
    {
        memoSlotsToReserve=slotsToReserve;

        doubleFrameInformation();
        slotReservation(frameInformationLength-1,0);
        
        fi=prepareOutgoingFI(listenedNodes,frameInformation);
        frameInfAndInfoTab=createPacket(2,0,fi);
    }
    else
    {
    if(slotsToReserve>0)
    {
        memoSlotsToReserve=slotsToReserve;
        
        slotReservation(frameInformationLength-1,0);
        
        fi=prepareOutgoingFI(listenedNodes,frameInformation);
        frameInfAndInfoTab=createPacket(2,0,fi);

    }
        else
        {
            
        }

    }
      

 
    
    memoSlotsToReserve=slotsToReserve;

    
    /*Save the frame information for the extra slots*/
    memoFI=frameInformation;
   
    
    NS_LOG_INFO("LOCAL    FI OF NODE : "<<node_id<<"->");
    stampaFI(frameInformation);
    NS_LOG_INFO("OUTGOING FI OF NODE : "<<node_id<<"->");
    stampaFI(fi);

    
    
    m_socket->Send (frameInfAndInfoTab);
    
    NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds ()
                 << " node "<<node_id<<" sent "
                 <<  frameInfAndInfoTab->GetSize() << " bytes ");
     
      
    for (uint32_t i=0; i<packets.size(); i++) {
        m_socket->Send (packets[i]);
        NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds ()
                     << " node "<<node_id<<" sent "
                     <<  packets[i]->GetSize() << " bytes ");
     
    
    }

    
 
    //erase Listened nodes list
    listenedNodes.erase(listenedNodes.begin(),listenedNodes.end());
    
    //ERASE PUBLIC TABLE
        publicTable.erase(publicTable.begin(),publicTable.end());
        mapListenedNodes.erase(mapListenedNodes.begin(),mapListenedNodes.end());
        for(uint32_t i=0;i<fileList.size();i++)
            insertNodeEntryInPublicTableFileInfo(fileList[i].file_name,node_id,batteryPercentage);

   

      //deve azzerare il FI(trane per la posizione del nodo corrente) in modo da costruire uno nuovo.
    for (std::vector<BasicChannel>::iterator it = frameInformation.begin() ; it != frameInformation.end(); ++it)
    {
        if(it->node_name!=node_id)
        {
            it->node_name=-1;
            it->free=true;
        }
    }
    
    ScheduleStopTransmissionEvent ();

    
    
 checkIfnodeIsFarFromAreaOfInterest();

    
   }



//*** Gestisce la ricezione dei pacchetti
void AirCacheApplication::HandleRead (Ptr<Socket> socket)
{

  NS_LOG_FUNCTION (this);
    

  Ptr<Packet> packet;
  Address address;

  while (packet = socket->RecvFrom(address))
    {

     if(packet!=0&&address!=local&&!sending)
     {  NS_LOG_INFO (" At time " << Simulator::Now ().GetSeconds ()<<" node "<<node_id<<" received " << packet->GetSize ());

      handlePacket(packet);

     }
   
    }


}

void AirCacheApplication::mergeFI(std::vector<BasicChannel> fI,uint32_t n_id)
{
    bool myCollision=false;
    bool doubled=false;
    uint32_t searchBound=0;
    
    

  
    /*HANDLE THE DOUBLE FI*/

    if(fI.size()>frameInformation.size())
    {
        for (uint32_t i=frameInformation.size(); i<fI.size(); i++)
        {
            if(fI[i].free==true && frameInformation[i-frameInformationLength].free==false)
            {
                frameInformation.push_back(frameInformation[i-frameInformationLength]);
            }
            else
            {
                frameInformation.push_back(fI[i]);

            }
        }
        
        frameInformationLength=frameInformation.size();
        doubled=true;
        
    }
    
    if(frameInformationLength<fI.size())
    {
        searchBound=frameInformationLength;
    }
    else
    {
        searchBound=fI.size();
    }
    

  for(uint32_t i=0;i<searchBound;i++)
        {
                if(fI[i].free==false)
                {

                   if(frameInformation[i].free==true)
                      {
                          frameInformation[i]=fI[i];

                      }
                   else
                   { uint32_t n1=frameInformation[i].node_name;
                       uint32_t n2=fI[i].node_name;
                        if(n1!=n2)
                         {
                             /*If one of the nodes is in the listened nodes list then a collision occour, otherwise the nodes are far away from each other*/
                             if(i==(uint32_t)basicChannelPosition || searchInListenedNodes(listenedNodes,n1)==true||searchInListenedNodes(listenedNodes, n2)==true)
                             {
                                 frameInformation[i].node_name=-1;
                                 frameInformation[i].free=true;
                             }
                             
                             if(i==(uint32_t)basicChannelPosition)
                             {
                                 myCollision=true;

                             }
                         }
                                
                    }

                        
                }
           
                else
                {/*Do what the node explicitly say in case of extra slot reservation*/
                if(frameInformation[i].node_name==(int)n_id)
                  {

                      frameInformation[i]=fI[i];

                   }
        }
    }
    
    //if the node is in the part of the FI discarded by the halveFI process
    if(basicChannelPosition>(int)fI.size())
        myCollision=true;
    
    
    
    if(myCollision)
    {
        frameInfNodeChoose();
        ScheduleStartTransmissionEvent();
    }
    
    
    
    
    /* When the FI received by node n_id is the double of my local FI, then it is neccessaru to check if the n_id
     position in FI is after my position in FI, if this is the case, a new postponed event is needed and the presvious scheduled event has to be deleted */
    
    
    if(doubled)
    {
        Time t=Simulator::GetDelayLeft(m_startStopSendEvent);
        if(Simulator::Now()+t>=frameInfBeginTime)
        {
            frameInfBeginTime+=MilliSeconds((frameInformationLength/2)*(basicChannelLength.GetMilliSeconds()));
            Simulator::Cancel (m_startStopSendEvent);
            ScheduleStartTransmissionEvent();
            
        }
            
    }
        
       

}

void AirCacheApplication::handleFile(ACFile file)
{
//handle the file and update the public table etc...
  if(!searchFile(file))
  {
    fileList.push_back(file);
    ListOfFileNames.push_back(file.file_name);
    updatePublicTable(file.file_name,batteryPercentage,node_id);
      
      //is a traceSourceaccessor to show in NetAnim that the node own at least one File 
      ownFile=true;
      
     NS_LOG_INFO("fileList Size"<<fileList.size());
  }
  //else discard file, do nothing


}


bool AirCacheApplication::searchFile(ACFile file){
bool found=false;
for(uint32_t i=0;i<ListOfFileNames.size();i++)
 {
        if(file.file_name==ListOfFileNames[i]) 
             {found=true;i=ListOfFileNames.size();}
 }

return found;
}




Ptr<Packet> AirCacheApplication::createPacket(uint32_t cases,uint32_t indice,std::vector<BasicChannel> fi){

     Ptr<Packet> packet = Create<Packet> (0);
       
    AcHeader fIHeader=AcHeader(cases,node_id);
    packet->AddHeader(fIHeader);

switch(cases)
{

 // frame information + infoTable
  case 2 :
    {
           
        InfoTableHeader infTabHead=InfoTableHeader(ListOfFileNames,fi,frameInfBeginTime.ToInteger(Time::NS),batteryPercentage);
        packet->AddTrailer(infTabHead);
    
    }
    break;

  //publicTable
  case 3 :
    {
             PublicTableHeader pubTabHeader=PublicTableHeader(publicTable,cases);
             packet->AddTrailer(pubTabHeader);
        
    }
        
    break;

  //file
  case 4 :
   {
     if(fileList.size()!=0)
  {  AcFileHeader fileHeader=AcFileHeader(fileList[indice]);
   packet->AddTrailer(fileHeader);}
   }
    break;
}

return packet;
}



void AirCacheApplication::handlePacket(Ptr<Packet> packet)
{
    AcHeader header;
    packet->RemoveHeader (header);
    uint16_t node;
    uint32_t cases=header.GetData(node);

   
switch(cases)
{
      // fragmented Packet
    case 1:
    {


        FragHeader fragTrail;
        packet->RemoveTrailer(fragTrail);

        uint32_t pack_id;
        uint32_t pack_seq_num=fragTrail.GetData(pack_id);

        int pos=searchIdPacketIn_Incoming(pack_id);
        if(pos!=-1)
        {
            inComingPackets[pos].packetMap.insert(std::pair<uint32_t,Ptr<Packet> >(pack_seq_num,packet));

        }
        else
        {

            std::map<uint32_t,Ptr<Packet> > mapPack;
            mapPack.insert(std::pair<uint32_t,Ptr<Packet> >(pack_seq_num,packet));

            inComingPackets.push_back(InComingPackets(pack_id, mapPack));


        }
        
        // Check if all the fragments of the Packet was sent
        if(pack_seq_num==1)
        {  

            Ptr<Packet> reconstructedPacket=retrievePacket(pack_id);

           NS_LOG_INFO("********************1*******************");
            
            handlePacket(reconstructedPacket);

            
        }
            }
        break;
        
        
 // frame information + infoTable
  case 2 :
      {
          InfoTableHeader infTabHead;
          packet->RemoveTrailer(infTabHead);
          uint64_t timeRetr;
          uint8_t batery;
          std::vector<BasicChannel> fi;
          std::vector<std::string> listOfFilesReceived;
          
          
          listOfFilesReceived=infTabHead.GetData(fi,timeRetr,batery);
          
         

          updateListenedNodesLists(node);
          
          uint16_t listNodeSize=calcListenedNodes(fi);
          updateNodeLinkedMap(node,listNodeSize);
          
          handleFrameInformation(fi,timeRetr,node);

          //elimina tutte le occorrenze del nodo nella public table per inserire le nuove
          for(uint32_t i=0;i<publicTable.size();i++)
            if(publicTable[i].occurrences.find(node)!=publicTable[i].occurrences.end())
                publicTable[i].occurrences.erase(node);


          
          
          for(uint32_t i=0;i<listOfFilesReceived.size();i++)
           {
                 updatePublicTable(listOfFilesReceived[i],batery,node);
           }
          
       }
    break;

  //frame information+ publicTable
  case 3 :
        
    {

        PublicTableHeader pubTabHeader;
        packet->RemoveTrailer(pubTabHeader);
        std::vector<FileInfo> pT=pubTabHeader.GetData();
        if(!pT.empty())
            handlePublicTable(pT,node);
    }
    break;

  //frame information + file
  case 4 :
  {

      AcFileHeader fileHeader;
      packet->RemoveTrailer (fileHeader);
      ACFile file=fileHeader.GetData();
      handleFile(file);}
      break;
}
 
}

void AirCacheApplication::handleFrameInformation(std::vector<BasicChannel> fi,uint64_t timeRetr,uint32_t n_id)
{

 

          if(initialized==false)
             {  frameInformation=fi;
                frameInfBeginTime=Time::FromInteger(timeRetr,Time::NS);
                NS_LOG_INFO("frameInfBeginTime.ToDouble(Time::S)-nodo "<<node_id<<"  "<<frameInfBeginTime);
                frameInformationLength=frameInformation.size();
                frameInfNodeChoose();
                initialized=true;
                stampaFI(frameInformation);
                ScheduleStartTransmissionEvent ();
             }
        //altrimenti 
        else{
            
                mergeFI(fi,n_id);
         }

}

 bool AirCacheApplication::searchPublicTableFileInfo(std::string fileN)
{
  for(uint32_t i=0;i<publicTable.size();i++)
  {
        if(fileN==publicTable[i].fileName)// exist a fileinfo for this file in public table
         {
              return true;
         }
  }
  
return false;

}


void AirCacheApplication::insertNodeEntryInPublicTableFileInfo(std::string fileN,uint32_t node,uint32_t batt)
{
  bool existFileInfo=false;
   for(uint32_t i=0;i<publicTable.size();i++)
  {
        if(fileN==publicTable[i].fileName)// exist a fileinfo for this file in public table
         {  
            existFileInfo=true;
            std::map<uint32_t,uint32_t>::iterator it;
            it=publicTable[i].occurrences.find(node);
            if(it==publicTable[i].occurrences.end()) // if an entry do not exist then insert one
              {
                std::map<uint32_t,uint32_t>::value_type x(node,batt);
                publicTable[i].occurrences.insert(x);
                //after inserting the file in the public table check if it is in the required range (min,max)
                //if not  remove the file from FileList and listOfFiles.
                /*********************************************************************************************/
                if(maxValueReplicaData(i))
                {// delete the file, filename,public table entry;
                  deleteFileAndTraces(fileN);

                }
                


 
                
              }
           /* else  // an entry exist, replace it(needed if the battery % change in time) 
             {  //inserire nel codice se si implementa la funzione che cambia la durata della batteria
               publicTable[i].occurrences.erase(it);
               std::map<uint32_t,uint32_t>::value_type x(node,batt);
               publicTable[i].occurrences.insert(x);
             }
           */
         }
      
  }
   if(!existFileInfo) // do not exist an entry for this file, create one
           {
                std::map<uint32_t,uint32_t> occ;
                std::map<uint32_t,uint32_t>::value_type x(node,batt);
                occ.insert(x);
                publicTable.push_back(FileInfo(fileN,occ));
           }
}

void AirCacheApplication::updatePublicTable(std::string fileName,uint32_t bat,uint32_t node)
{
    
   //If a file info entry do not exist create it
  if(!searchPublicTableFileInfo(fileName))
   {
   std::map<uint32_t,uint32_t> occ;
   std::map<uint32_t,uint32_t>::value_type x(node,bat);
   occ.insert(x);
   publicTable.push_back(FileInfo(fileName,occ));
   }
 else // a fileInfo exists and the relative map element for this node has to be inserted
  {
   insertNodeEntryInPublicTableFileInfo(fileName,node,bat); 
  }

    
    
//after doing the updatePublicTable we have to check if the node position is bigger than the upper bound or lower than the lower-bound
  

}


void AirCacheApplication::printPublicTable()
{
  NS_LOG_INFO("********************************-NODE- "<<node_id<<" PUBLIC-TABLE INFORMATION ***********************");
 for(uint32_t i=0;i<publicTable.size();i++)
 {   NS_LOG_INFO("Fileinfo file "<< publicTable[i].fileName);
        for (std::map<uint32_t,uint32_t>::iterator it=publicTable[i].occurrences.begin(); it!=publicTable[i].occurrences.end(); ++it)
                NS_LOG_INFO(it->first << " => " << it->second);
 }
NS_LOG_INFO("******************************************************************************************************");
    
    
}



bool AirCacheApplication::maxValueReplicaData(uint32_t i){

 uint32_t pos=0;

 std::multimap<uint32_t,uint32_t> multMapSort;
for(std::map<uint32_t,uint32_t>::iterator itM=publicTable[i].occurrences.begin();itM!=publicTable[i].occurrences.end();++itM)
  multMapSort.insert ( std::pair<uint32_t,uint32_t>((*itM).second,(*itM).first) );


 uint32_t k=1;
 std::multimap<uint32_t,uint32_t>::reverse_iterator rit;
  for (rit=multMapSort.rbegin(); rit!=multMapSort.rend(); ++rit)
  {    
        
        if((*rit).second==(uint32_t)node_id)
          pos=k;
k++;
  }
k=0;
if(pos>4)
return true;
else
return false;

}


void AirCacheApplication::deleteFileAndTraces(std::string fileN)
{


  //delete file from list of files
bool esci=false;
for (std::vector<ACFile>::iterator it = fileList.begin()  ; it != fileList.end() && esci==false; it++)
  {      
        if((*it).file_name==fileN && fileN!=fileName)
         {fileList.erase(it);esci=true;

 }}
esci=false;
//delete filename from list of file names
for (std::vector<std::string>::iterator it = ListOfFileNames.begin() ; it != ListOfFileNames.end() && esci==false; it++)
    if((*it)==fileN && fileN!=fileName)
      {ListOfFileNames.erase(it);esci=true;}

//delete entry from publicTable
 for(uint32_t i=0;i<publicTable.size();i++)
  if(publicTable[i].fileName==fileN && fileN!=fileName)
    {publicTable[i].occurrences.erase(node_id);}


}

//il frame information che si invia si costruisce in base ai nodi che si riceve.
    std::vector<BasicChannel> AirCacheApplication::prepareOutgoingFI(std::vector<int> lNodes,std::vector<BasicChannel> FI)
{
    std::vector<BasicChannel> outFI=FI;

    for(uint32_t i=0;i<outFI.size();i++)
    {
     if(outFI[i].free==false && outFI[i].node_name!=node_id && !searchInListenedNodes(lNodes,outFI[i].node_name) )
     {
        outFI[i].node_name=-1;
        outFI[i].free=true;
     }
        
    }
            return outFI;
    
}
    
    void AirCacheApplication::updateListenedNodesLists(uint32_t nodeID)
    {
        
        bool found=false;
        for (std::vector<int>::iterator it = listenedNodes.begin() ; it != listenedNodes.end(); ++it)
        { if(*it==(int)nodeID)
        {found=true;}
        }
        //si memorizzano i nodi da cui si riceve.
        
        if(!found) //is a new node in the cluster
        {
            listenedNodes.push_back(nodeID);
        }
        
        
        
    }
    

    
    
    void AirCacheApplication::updateNodeLinkedMap(uint32_t n,uint32_t pubTableSize){
        
        if(mapListenedNodes.find(n)==mapListenedNodes.end())
        {mapListenedNodes.insert ( std::pair<uint32_t,uint32_t>(n,pubTableSize) );
        }
        else
        {   
             mapListenedNodes.erase(n);
             mapListenedNodes.insert ( std::pair<uint32_t,uint32_t>(n,pubTableSize) );
        }

        
        
    }
    
    
    bool AirCacheApplication::mostLinkedNode(){
        

    updateNodeLinkedMap(node_id,listenedNodesSize.size());
        
    std::multimap<uint32_t,uint32_t> multMap;
    
    for(std::map<uint32_t,uint32_t>::iterator it=mapListenedNodes.begin();it!=mapListenedNodes.end();++it)
    {      
        multMap.insert ( std::pair<uint32_t,uint32_t>(it->second,it->first) );
    }

             if(node_id==(int)(multMap.rbegin())->second)
               return true;
        
        return false;
         }
        
        
        
        
    void    AirCacheApplication::handlePublicTable(std::vector<FileInfo> pt,uint32_t node)
    {
        //before inserting the nodes public table delete its content from my public table
        for(uint32_t i=0;i<publicTable.size();i++)
        {
            if(publicTable[i].occurrences.find(node)!=publicTable[i].occurrences.end())
                publicTable[i].occurrences.erase(node);
        }
        
        for(uint32_t i=0;i<pt.size();i++)
        {
            if(!searchPublicTableFileInfo(pt[i].fileName))
                publicTable.push_back(pt[i]);
            else
            {
                for (std::map<uint32_t,uint32_t>::iterator it=pt[i].occurrences.begin(); it!=pt[i].occurrences.end(); ++it)
                {
                    int ret=retIndexPubTableGivenFilename(pt[i].fileName);
                    if(ret!=-1)
                        if(publicTable[ret].occurrences.find(it->first)==publicTable[ret].occurrences.end())
                            publicTable[ret].occurrences.insert(std::pair<uint32_t,uint32_t>(it->first,it->second));
                    
                }
            }
        }
        
        //Dopo aver fatto il merge di public table bisogna portare il sistema in equilibrio eliminando i file in eccesso
        
        
        
        for(uint32_t i=0;i<publicTable.size();i++)
        {
           if(maxValueReplicaData(i))
                    {

                        // delete the file, filename,public table entry;
                        deleteFileAndTraces(publicTable[i].fileName);
                        
                    }
        
        
    }
        
    }
        
        
        int  AirCacheApplication::retIndexPubTableGivenFilename(std::string fileNam)
    {
        for(uint32_t i=0;i<publicTable.size();i++)
        {
            if(publicTable[i].fileName==fileNam)
                return i;
        }
        return -1;
    }
    
    
   uint32_t AirCacheApplication::bytesSentInBasicChannel()
    {
        
        uint32_t BytesPerSec=(m_cbrRate.GetBitRate()-7000000)/8;
        uint32_t BytesPerMilliseconds=BytesPerSec/1000;
        return basicChannelLength.ToInteger(Time::MS)*BytesPerMilliseconds;
    }
    
    
    uint32_t AirCacheApplication::calculateNumbOfSlotsToReserve(uint32_t pckt_size)
    {
        uint32_t x=bytesSentInBasicChannel();
        uint32_t ret=0;
        ret=pckt_size/x;
        if (pckt_size%x!=0)
            ret+=1;
        
        
        
        return ret;
    }
/*
    void AirCacheApplication::reserveAllFreeSlots()
    {
        for(uint32_t i=0;i<frameInformation.size();i++)
        {
            if(frameInformation[i].free==true)
            {
                frameInformation[i].node_name=node_id;
                frameInformation[i].free=false;
            }
        }
    }
        
*/
uint16_t AirCacheApplication::calcListenedNodes(std::vector<BasicChannel> fi){
    
    uint16_t ret=0;
    for (uint16_t i=0; i<fi.size(); i++) {
        if (fi[i].free==false) {
            ret++;
        }
    }
    return ret;
}
    

   
    bool AirCacheApplication::searchInListenedNodes(std::vector<int> lNodes,uint32_t node_n){
        for (std::vector<int>::iterator it = lNodes.begin() ; it != lNodes.end(); ++it)
        {
            if(*it==(int)node_n)
                return true;
        }
        return false;
    }

    
    
    
    
    void AirCacheApplication::slotReservation(uint32_t upper,uint32_t lower){
        
   
      
        
        uint32_t leavefree=0;
        switch (frameInformationLength) {
            case 8:
                leavefree=1;
                break;
            case 16:
                leavefree=1;
                break;
            case 32:
                leavefree=1;
                break;
            case 64:
                leavefree=2;
                break;
            case 128:
                leavefree=8;
                break;
            case 256:
                leavefree=10;
                break;
            case 512:
                leavefree=12;
                break;
            case 1024:
                leavefree=14;
                break;
                
        }
        
        
        for (int k=upper; k>=(int)lower && (int)slotsToReserve>0; --k) {
        if (frameInformation[k].node_name==node_id && k!=basicChannelPosition)
        {
            if(k>basicChannelPosition)
            {
                
                frameInformation[k].free=false;
                frameInformation[k].node_name=node_id;
                slotsToReserve--;
                memoSlotsToReserve--;
                
                Time t=MilliSeconds((k-basicChannelPosition)*basicChannelLength.ToInteger(Time::MS));
              Simulator::Schedule (t, &AirCacheApplication::sendFragment, this,k);
                
                
            }
            if(k<basicChannelPosition)
            {
                
                frameInformation[k].free=false;
                frameInformation[k].node_name=node_id;
                slotsToReserve--;
                memoSlotsToReserve--;
                
                uint32_t t1=(frameInformationLength-(basicChannelPosition))*basicChannelLength.ToInteger(Time::MS);
                uint32_t t2=k*basicChannelLength.ToInteger(Time::MS);
                Time t=MilliSeconds(t1 + t2);
                
         Simulator::Schedule (t, &AirCacheApplication::sendFragment, this,k);
                
            }

        }
        
        }
        
        
            for (int k=upper; k>=(int)lower && (int)slotsToReserve>0; --k) {
                
                    /*Occupy the free slots*/
                if (frameInformation[k].free==true)
                {
                   if(leavefree==0)
                   {
                    if(k>basicChannelPosition)
                    {

                        frameInformation[k].free=false;
                        frameInformation[k].node_name=node_id;
                        slotsToReserve--;
                        memoSlotsToReserve--;

                        Time t=MilliSeconds((k-basicChannelPosition)*basicChannelLength.ToInteger(Time::MS));
                     Simulator::Schedule (t, &AirCacheApplication::sendFragment, this,k);

                       
                    }
                   if(k<basicChannelPosition)
                    {

                        frameInformation[k].free=false;
                        frameInformation[k].node_name=node_id;
                        slotsToReserve--;
                        memoSlotsToReserve--;

                        uint32_t t1=(frameInformationLength-(basicChannelPosition))*basicChannelLength.ToInteger(Time::MS);
                        uint32_t t2=k*basicChannelLength.ToInteger(Time::MS);
                        Time t=MilliSeconds(t1 + t2);
                        

                       Simulator::Schedule (t, &AirCacheApplication::sendFragment, this,k);

                    }
                    

                    
                   }
                    else
                    {
                        leavefree--;
                    }
                }
                
        }

        
    }
    
    void AirCacheApplication::sendFragment(uint32_t k)
        {
                       Ptr<Packet> fragPacket;
            

            if(!outGoingPacketsList.empty())
            fragPacket=outGoingPacketsList.front().removeAndReturnLastElement();

            if(fragPacket!=0)
            {
                sending=true;
                outPacketsList.push_back(fragPacket);
                /*WE NEED TO ADD THE HEADER */

                AcHeader fIHeader=AcHeader(1,node_id);
                fragPacket->AddHeader(fIHeader);

                //insert the packet id and the sequence number(since this number is decremented by the removeAnReturnLastElement +1 is needed)
                FragHeader fragHeader=FragHeader(outGoingPacketsList[0].packet_id,(outGoingPacketsList[0].pack_Tot_Num+1));
                fragPacket->AddTrailer(fragHeader);



                m_socket->Send(fragPacket);
                NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds ()
                             << " node "<<node_id<<" sent "
                             <<  fragPacket->GetSize() << " bytes ");
                /*The slot is set to free only if we do not need it in the next FI*/
               
                

                if(memoSlotsToReserve==0)
                {
                    frameInformation[k].free=true;
                    memoFI[k].free=true;
                    frameInformation[k].node_name=-1;
                    memoFI[k].node_name=-1;
                }
                else
                {
                    
                    memoSlotsToReserve--;

                }
                
                std::vector<BasicChannel> fi=prepareOutgoingFI(memolistenedNodes,memoFI);
                
                Ptr<Packet> frameInfAndInfoTab=createPacket(2,0,fi);
                m_socket->Send(frameInfAndInfoTab);
                 NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds ()
                             << " node "<<node_id<<" sent "
                             <<  frameInfAndInfoTab->GetSize() << " bytes ");

               
                
            }
            else
            {
                if(!outGoingPacketsList.empty())
                outGoingPacketsList.erase(outGoingPacketsList.begin());
            }
            
            
            if(outGoingPacketsList.front().pack_Tot_Num<=0)
                outGoingPacketsList.erase(outGoingPacketsList.begin());
            
            sending=false;
            
        }
  
  
    
    int AirCacheApplication::searchIdPacketIn_Incoming(uint32_t p_id){
        
        for (uint32_t i=0; i<inComingPackets.size(); i++)
        {
            if(inComingPackets[i].packet_id==p_id)
                return i;
                
        }
        return -1;
        
    }
    
    Ptr<Packet> AirCacheApplication::retrievePacket(uint32_t pack_id){
        
        Ptr<Packet> ret;

        for (uint32_t i=0; i<inComingPackets.size(); i++)
        {

            if(inComingPackets[i].packet_id==pack_id)
            {

                std::map<uint32_t,Ptr<Packet> >::reverse_iterator rit=inComingPackets[i].packetMap.rbegin();
                ret=rit->second;
                rit++;

                for (; rit!=inComingPackets[i].packetMap.rend(); ++rit)
                {
                    ret->AddAtEnd(rit->second);

                }
            }
        }

        return ret;
        
    }
    
    void AirCacheApplication::doubleFrameInformation()
    {
        
       
        for (uint32_t i=0; i<frameInformationLength; i++)
        {
            //if the node in FI is one not heard directly put it in the new part of FI
            if(frameInformation[i].free==false && !searchInListenedNodes(listenedNodes, frameInformation[i].node_name)&& frameInformation[i].node_name!=node_id)
            {
                frameInformation.push_back(frameInformation[i]);
            }
            else
            {
                frameInformation.push_back(BasicChannel(-1,true,basicChannelLength));
            }
            
                
            
        }
        frameInformationLength=frameInformation.size();
        outFILength=frameInformationLength;
        
        
    }
    
    
    void AirCacheApplication::halveFrameInformation()
    {
        for (uint32_t i=frameInformationLength-1; i>=(frameInformationLength/2); i--) {
            frameInformation.pop_back();
        }
        frameInformationLength=frameInformationLength/2;
        outFILength=frameInformationLength;
        

        
    }
    
    bool AirCacheApplication::filePresentInListenedNodes(std::string fileName)
    {
        std::map<uint32_t,uint32_t>::iterator it;
        bool presentInALL=true;
        
        if (listenedNodes.size()==0) {
            presentInALL=false;
        }
        for (uint32_t i=0; i<listenedNodes.size();i++)
        {
            for (uint32_t j=0; j<publicTable.size(); j++)
            {
                if(fileName==publicTable[j].fileName)
                {
                    it=publicTable[j].occurrences.find(listenedNodes[i]);
                    if(it==publicTable[j].occurrences.end())
                    {
                        presentInALL=false;
                    }

                }
            }
        }
            

        return presentInALL;
    }
    
    std::string AirCacheApplication::getfileNameInPacketType4(Ptr<Packet> pack){
        
        Packet p=Packet(*pack);
        AcHeader header;
        p.RemoveHeader (header);
        uint16_t node;
        uint32_t cases=header.GetData(node);
        if(cases==4)
        {
        AcFileHeader fileHeader;
        p.RemoveTrailer (fileHeader);
        ACFile file=fileHeader.GetData();
        return file.file_name;
        }
        return "";
    
    }

    bool AirCacheApplication::searchFileNameInOutGoingPackets(std::string fN)
    {
        for (uint32_t i=0; i<outGoingPacketsList.size(); i++) {
            if (outGoingPacketsList[i].fileName==fN) {
                return true;
            }
        }
        return false;
    }
    
    uint32_t AirCacheApplication::numbOfEffectiveNodes(std::vector<BasicChannel> frInf)
    
    {
        
        
        
        
         //Check if the FI was doubled due to high number of slots or due to extra slot reservation
         double numbOfEffectiveNodes=0;
         std::map<uint32_t,std::string> map;
         std::map<uint32_t,std::string>::iterator it;
         
         for (uint32_t i=0; i<frInf.size(); i++)
         {
         it=map.find(frInf[i].node_name);
         if(it==map.end()&&frInf[i].node_name!=-1)
         {
         numbOfEffectiveNodes++;
         map.insert ( std::pair<uint32_t,std::string>(frInf[i].node_name,"present"));
         }
         
         }
         
        
        return numbOfEffectiveNodes;
        
    }

    void AirCacheApplication::printListofFileNames()
{
    
    IntegerValue n=(uint32_t)node_id;
    std::string fileNameOutput="SimOutput/FilesOwnedByEachNode.txt";
    std::ofstream myfile(fileNameOutput.c_str(),std::ios::app);
    if(!myfile.is_open())
    myfile.open (fileNameOutput.c_str());
    
    myfile<<"Files owned by Node: "<<node_id<<std::endl;
    for(uint32_t i=0;i<ListOfFileNames.size();i++)
    {
        myfile<<ListOfFileNames[i];
    }
    myfile<<std::endl;

}
    
    bool AirCacheApplication::nodeInAreaOfInterest(){
        
        Ptr<MobilityModel> mob;
        mob=GetNode()->GetObject<MobilityModel>();
        Vector pos = mob->GetPosition ();
        
        double deltaX=pos.x-aoi_x;
        double deltaY=pos.y-aoi_y;
        
        double distance=std::sqrt(deltaX * deltaX + deltaY * deltaY);
    if(distance<=dist)
    {
        return true;
    }
        else
            return false;
       
        
    }
    
    void AirCacheApplication::lookingMode()
    {

        NS_ASSERT (m_looking_mode1.IsExpired ());
        NS_ASSERT (m_looking_mode2.IsExpired ());
        NS_ASSERT (m_looking_mode3.IsExpired ());

        if(nodeInAreaOfInterest())
        {
            
            if(!initialized)
            {
                
                CreateSockets();
                m_joining_ac2=Simulator::Schedule (MilliSeconds(5125), &AirCacheApplication::joinAirCache, this);
            }
            
        }
        else
        {
            m_looking_mode1=Simulator::Schedule (MilliSeconds(500), &AirCacheApplication::lookingMode, this);

        }
        
        
        
    }
    
    bool AirCacheApplication::checkIfnodeIsFarFromAreaOfInterest()
    {
        Ptr<MobilityModel> mob;
        mob=GetNode()->GetObject<MobilityModel>();
        Vector pos = mob->GetPosition ();
        
        double deltaX=pos.x-aoi_x;
        double deltaY=pos.y-aoi_y;
        
        double distance=std::sqrt(deltaX * deltaX + deltaY * deltaY);
        if(distance>dist)
        {
            CancelSendEvents();
            closeSockets();

            initialized=false;
            eraseAllDataStructures();
            m_looking_mode2=Simulator::Schedule (MilliSeconds(500), &AirCacheApplication::lookingMode, this);
            return true;
            
        }
        else
            return false;
    }
    
    
    void AirCacheApplication::CreateSockets(){
        
        
        if (!s_socket)
        {
            s_socket = Socket::CreateSocket (GetNode (), m_tid);
            local = InetSocketAddress (GetNode()->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(),20);
            s_socket->Bind (local);
            
            s_socket->SetRecvCallback (MakeCallback (&AirCacheApplication::HandleRead, this));
        }
        
        
        //send
        if (!m_socket)
        {
            
            m_socket = Socket::CreateSocket (GetNode (), m_tid);
            //local = InetSocketAddress (GetNode()->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(),20);
            
            m_socket->SetAllowBroadcast (true);
            // m_socket->Bind (local);
            // m_socket->SetRecvCallback (MakeCallback (&AirCacheApplication::HandleRead, this));
            m_socket->Connect (InetSocketAddress(Ipv4Address("255.255.255.255"), 20));
            
            m_socket->ShutdownRecv ();
            
        }

        
    }
    
    void AirCacheApplication::closeSockets(){
        
        if(m_socket != 0)
        {
            m_socket->Close ();
        }
        else
        {
            NS_LOG_WARN ("AirCacheApplication found null socket to close in StopApplication");
        }
        
        if(s_socket != 0)
        {
            s_socket->Close ();
        }
        else
        {
            NS_LOG_WARN ("AirCacheApplication found null socket to close in StopApplication");
        }

        
    }
    
    
    void AirCacheApplication::eraseAllDataStructures(){
        frameInfBeginTime=Seconds(0);
        frameInformation.erase(frameInformation.begin(),frameInformation.end());
        memoFI.erase(memoFI.begin(),memoFI.end());
        memoSlotsToReserve=0;
        outGoingPacketsList.erase(outGoingPacketsList.begin(),outGoingPacketsList.end());
        outPacketsList.erase(outPacketsList.begin(),outPacketsList.end());
        inComingPackets.erase(inComingPackets.begin(),inComingPackets.end());
        slotsToReserve=0;
        basicChannelPosition=0;
        
    }

} // Namespace ns3
